﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Actya.Core.Infrastructure;
using Actya.Core.SiteStructure;

namespace Actya.Web.Mvc.Helpers
{
	public class NavigationHelper
	{
		private readonly ICmsContext _cmsContext;
		private readonly IPageService _pageService;

		public NavigationHelper(ICmsContext cmsContext, IPageService pageService)
		{
			_cmsContext = cmsContext;
			_pageService = pageService;
		}

		public MvcHtmlString RenderMainMenu(int defaultExpandLevel, int levelsToMergeWithRoot = 0)
		{
			return RenderMainMenu(defaultExpandLevel, null, levelsToMergeWithRoot);
		}

		public MvcHtmlString RenderMainMenu(int defaultExpandLevel, int? maxExpandLevel, int levelsToMergeWithRoot = 0)
		{
			var ulBuilder = new TagBuilder("ul");
			string rootList = GenerateNavigationListForNodes(this._cmsContext.Site.SitemapNodes, defaultExpandLevel, maxExpandLevel, levelsToMergeWithRoot);
			ulBuilder.InnerHtml += rootList;

			return MvcHtmlString.Create(ulBuilder.ToString());
		}

		public HtmlString RenderSubMenu(int level, int levelsToExpand = 0)
		{
			var activeNode = _cmsContext.GetCurrentSitemapNode();
			if (activeNode != null)
			{
				var ulBuilder = new TagBuilder("ul");
				IEnumerable<SitemapNode> sitemapNodesToShow = new List<SitemapNode>();
				if (activeNode.Level == level - 1)
				{
					sitemapNodesToShow = activeNode.SitemapNodes;
				}
				else if (activeNode.Level >= level && level > 0)
				{
					sitemapNodesToShow = activeNode.Trail[activeNode.Trail.Length - level].SitemapNodes;
				}
				else if (level == 0)
				{
					sitemapNodesToShow = _cmsContext.Site.SitemapNodes;
				}
				ulBuilder.InnerHtml += GenerateNavigationListForNodes(sitemapNodesToShow, level + levelsToExpand, null, 0);
				return MvcHtmlString.Create(ulBuilder.ToString());
			}
			return null;
		}

		private string GenerateNavigationListForNodes(IEnumerable<SitemapNode> sitemapNodes, int defaultExpandLevel, int? maxExpandLevel, int levelsToMergeWithRoot)
		{
			var activeNode = _cmsContext.GetCurrentSitemapNode();
			string itemsList = String.Empty;

			var filteredSitemapNodes = FilterSitemapNodes(sitemapNodes);

			foreach (var sitemapNode in filteredSitemapNodes)
			{
				var liBuilder = new TagBuilder("li");
				var aBuilder = new TagBuilder("a");
				aBuilder.SetInnerText(sitemapNode.Title);

				if (!String.IsNullOrEmpty(sitemapNode.Url))
				{
					aBuilder.MergeAttribute("href", GetAbsoluteUrl(sitemapNode.Url));
					if (activeNode == sitemapNode)
					{
						aBuilder.AddCssClass("current");
					}
				}
				else
				{
					aBuilder.AddCssClass("container");
				}
				liBuilder.InnerHtml = aBuilder.ToString();

				if ((sitemapNode.Level < defaultExpandLevel || (activeNode != null && activeNode.Trail.Contains(sitemapNode))) 
					&& sitemapNode.SitemapNodes.Any() && (maxExpandLevel == null || maxExpandLevel > sitemapNode.Level))
				{
					var childList = GenerateNavigationListForNodes(sitemapNode.SitemapNodes, defaultExpandLevel, maxExpandLevel, levelsToMergeWithRoot);
					if (sitemapNode.Level > levelsToMergeWithRoot -1)
					{
						var ulBuilder = new TagBuilder("ul");
						ulBuilder.InnerHtml += childList;
						liBuilder.InnerHtml += ulBuilder.ToString();
					}
					else
					{
						liBuilder.InnerHtml += childList;
					}
				}
				itemsList += liBuilder.ToString();
			}
			return itemsList;
		}

		private IEnumerable<SitemapNode> FilterSitemapNodes(IEnumerable<SitemapNode> sitemapNodes)
		{
			// Filter sitemap items that are related to a page based on publication, authorization etc.
			var allPageIds = sitemapNodes.Where(sn => sn.PageId != null).Select(sn => sn.PageId);
			var filteredPageIds = _pageService.GetPublishedAndAuthorizedPageIds(allPageIds.ToArray());
			var filteredSitemapNodes =
				sitemapNodes.Where(sn => sn.PageId == null || filteredPageIds.Contains(sn.PageId) && sn.ShowInNavigation);
			return filteredSitemapNodes;
		}

		public string GetAbsoluteUrl(string url)
		{
			if (url.Contains("://"))
			{
				// No processing when the url contains a protocol
				return url;
			}
			var absoluteUrl = url;
			if (! String.IsNullOrEmpty(_cmsContext.Host.Settings.PagesPrefix))
			{
				absoluteUrl = _cmsContext.Host.Settings.PagesPrefix + absoluteUrl;
			}
			if (!absoluteUrl.StartsWith("/"))
			{
				absoluteUrl = "/" + absoluteUrl;
			}
			return VirtualPathUtility.ToAbsolute("~" + absoluteUrl);
		}
	}
}